/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simulation;

import jade.core.AID;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Vector;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import logging.LogSetup;
import org.apache.log4j.Logger;
import persistence.DataReader;
import persistence.DataWriter;

/**
 *
 * @author Danielondon
 */
public class SimulationModel extends Observable {
    protected Logger logger = LogSetup.getLogger();
    public Vector<AgentInfo> carAgentsInfo;
    private DefaultTableModel tableModel;
    private int totalSlots;
    private String nameAgent = "carAgent";
    private int counterCars = 0;    
    private int INDEX_AGENT_NAME = 0;
    private int INDEX_CAR_MODEL = 1;
    private int INDEX_CAR_SPECS = 2;
    private int INDEX_DRIVING_PATTERN = 3;
    private int INDEX_CHARGING_STRATEGY = 4;
    private int INDEX_MARKET = 5;
    private int INDEX_STATE = 6;
    private int INDEX_SLOT_COUNT = 7;
    private int INDEX_IS_SIMULATION_RUNNING = 8;
    private int INDEX_COMMENTS = 9;
    private AID marketAgentId;
    private Map<String, SimulationResult> results;
    private Map<String, SimulationResult> resultByAgent;
    private String STRING_SEPARATOR = ",";
    private int slotSize;
    private String wildCardFileName = "FILE_NAME";
    private String templateFile = "templatePlot.txt";
    private String templateFile2 = "templatePlot2.txt";
    
    /**
     * Creates the Simulation Model
     */
    public SimulationModel(){
        this.carAgentsInfo = new Vector();
        Object [] columns =new Object[] {"Name", "Car Model","Car Specs", "Driving Pattern", "Charging Strategy", "Market", "Car State", "Slot", "Simulating?", "Comments"};
        tableModel = new DefaultTableModel(columns, 0);  
        results = new HashMap<String, SimulationResult>();
        resultByAgent = new HashMap<String, SimulationResult>();
    }

    void addCar(String agentName, String carModel, String drivingPattern, String chargingStrategy) {
        AgentInfo agentInfo = new AgentInfo(agentName, carModel);
        agentInfo.setChargingStrategy(chargingStrategy);
        agentInfo.setDrivingPattern(drivingPattern);
        carAgentsInfo.add(agentInfo);
        tableModel.addRow(new Object[] { null, null, null, null, null, null, null, null, null, null });
        this.updateTable(agentInfo, tableModel.getRowCount() - 1);    
        this.setChanged();
    }

    public TableModel getTableModel(){        
        return tableModel;
    }
    
    public int getCarsCount() {
        return this.carAgentsInfo.size();
    }
    
    public void setAgentName(int index, String name) {                
        AgentInfo agent = this.carAgentsInfo.get(index);
        agent.setName(name);
        updateTable(agent, index);
    }

    boolean isSimulationStarted() {
        for (AgentInfo info:this.carAgentsInfo){
            if (info.getIsSimulationRunning())
                return true;
        }
        
        return false;
    }

    public AgentInfo getCarAgent(int i) {
        return this.carAgentsInfo.get(i);
    }

    /**
     * @return the totalSlots
     */
    public int getTotalSlots() {
        return totalSlots;
    }

    /**
     * @param totalSlots the totalSlots to set
     */
    public void setTotalSlots(int totalSlots, int slotSize) {
        this.totalSlots = totalSlots;
        this.slotSize = slotSize;
    }


    public String getAgentName() {
        return nameAgent + "-" + counterCars++;
    }

    public AgentInfo getAgentInfoFromSender(AID sender) {
        String name = sender.getLocalName();
        return this.getCarAgentInfo(name);
    }
    public void updateModel(AID sender, String message) {
        String[] tokens = message.split(CommonResources.STRING_SEPARATOR);
        
        AgentInfo agent = this.getAgentInfoFromSender(sender); 
        if (agent == null) {
            return;
        }
        
        agent.setId(sender);
        // The following IDs should match the ones used in Create Message of CarAgent
        agent.setMarket(tokens[0]);
        agent.setStatus(tokens[1]);
        agent.setSlotCount(tokens[2]);
        agent.setIsSimulationRunning(Boolean.valueOf(tokens[3]));
        agent.setComments(tokens[4]);
        agent.setCarModel(tokens[5]);
        this.updateTable(agent);
        
    }
    
    private AgentInfo getCarAgentInfo(String name){
        for(AgentInfo a:this.carAgentsInfo){
            if (a.getName().equals(name)){
                return a;
            }
        }
        
        return null;
    }
    
    private void updateTable(AgentInfo agent){
        if (agent != null) {
            for (int i = 0; i < tableModel.getRowCount(); i++ ){
                if (tableModel.getValueAt(i, INDEX_AGENT_NAME).equals(agent.getName())){
                    this.updateTable(agent, i);
                    return;
                }
            }
        }
    }    
    
    private void updateTable(AgentInfo agent, int index){
        tableModel.setValueAt(agent.getName(), index, INDEX_AGENT_NAME);
        tableModel.setValueAt(agent.getCarModel(), index, INDEX_CAR_MODEL);
        tableModel.setValueAt(agent.getCarSpecs(), index, INDEX_CAR_SPECS);
        tableModel.setValueAt(agent.getDrivingPattern(), index, INDEX_DRIVING_PATTERN);
        tableModel.setValueAt(agent.getChargingStrategy(), index, INDEX_CHARGING_STRATEGY);
        tableModel.setValueAt(agent.getMarket(), index, INDEX_MARKET);        
        tableModel.setValueAt(agent.getState(), index, INDEX_STATE);
        tableModel.setValueAt(agent.getSlotCount(), index, INDEX_SLOT_COUNT);
        tableModel.setValueAt(agent.getIsSimulationRunning(), index, INDEX_IS_SIMULATION_RUNNING);
        tableModel.setValueAt(agent.getComments(), index, INDEX_COMMENTS);
    }

    public void setMarket(AID marketAgentId) {
        this.marketAgentId = marketAgentId;
    }
    
    public boolean isMarketReady(){
        return this.marketAgentId != null;
    }
    
    public AID getMarket(){
        return this.marketAgentId;
    }

    void addResult(AID sender, SimulationResult result) {
        AgentInfo agent = this.getAgentInfoFromSender(sender); 
        if (agent == null) {
            return;
        }
        this.resultByAgent.put(agent.getName(), result);
        this.results.put(result.getName(), result);       
        logger.info("Amount of Results " + this.results.size());
    }
    
    public boolean resultsToFile(String fileName, String extensionResults, String extensionPlot) {
        try {
            double totalPerSlot, marketPrice;
            DataWriter dataWriter = null;
            dataWriter = new DataWriter(fileName + extensionResults, true);
            String resultRow;
            SortedMap <Double, List<Double>> map = new TreeMap();
            
            for (int i =0;  i < totalSlots; i ++){
                resultRow = i + STRING_SEPARATOR;
                totalPerSlot = 0;
                marketPrice = 0;
                for (SimulationResult result : this.results.values()){
                    if (result.getSlots() == totalSlots){
                        totalPerSlot += result.getCharging()[i]; 
                        if (marketPrice == 0){
                            marketPrice = result.getMarket()[i];
                        }
                    }                
                }   
                resultRow += marketPrice + STRING_SEPARATOR ;
                resultRow += totalPerSlot + STRING_SEPARATOR ;
                
                if (!map.containsKey(marketPrice))
                    map.put(marketPrice, new ArrayList());                
                
                for (SimulationResult result : this.results.values()){
                    if (result.getSlots() == totalSlots){
                        //resultRow += result.getMarket()[i] + STRING_SEPARATOR;
                        resultRow += result.getCharging()[i] + STRING_SEPARATOR;
                                //result.getConsumption()[i] + STRING_SEPARATOR + 
                                //result.getCost()[i] + STRING_SEPARATOR ;
                        if (result.getCharging()[i] > 0)
                            map.get(marketPrice).add(result.getCharging()[i]);                        
                    }                
                }         

                dataWriter.write(resultRow);
            }
            
            this.createResultsFile(fileName, extensionPlot, this.templateFile2);
            this.createDataFile(fileName + "_aggregate", map, extensionResults);
            this.createResultsFile(fileName + "_aggregate", extensionPlot, this.templateFile);                        
            return true;
        } catch (Exception ex) {
            logger.error(ex);
            return false;
        }
    }

    public int getSlotSize() {
        return this.slotSize;
    }

    private void createResultsFile(String filename, String extensionPlot, String newFile) {
        try {
            String plotFile = filename + extensionPlot;
            DataReader reader = new DataReader(newFile);
            DataWriter writer = new DataWriter(plotFile, true);
            List<String> data = reader.getDataWithoutSplit();
            for(String line:data){
                writer.write(CommonResources.replaceText(line, this.wildCardFileName, filename));        
            }
        } catch (Exception ex) {
            logger.error("Error creating results file ", ex);
        }
    }

    void setNewStrategy(String newStrategy, int[] carIndexes) {
        for (int i =0; i < carIndexes.length; i++){
            String name = (String)this.tableModel.getValueAt(carIndexes[i], INDEX_AGENT_NAME);
            AgentInfo info = this.getCarAgentInfo(name);
            info.setChargingStrategy(newStrategy);
            updateTable(info);
        }
        //this.setChanged();
    }

    private void createDataFile(String filename, SortedMap<Double, List<Double>> map, String extensionResults) {
        try {
            String resultsFile = filename + extensionResults;
            DataWriter writer = new DataWriter(resultsFile, true);
            Iterator<Double> iterator = map.keySet().iterator();
            String line;
            Double price;
            while (iterator.hasNext()){
                price = iterator.next();
                line = price.toString();
                line += (STRING_SEPARATOR + this.getTotal(map.get(price)));
                writer.write(line);
            }
        } catch (Exception ex) {
            logger.error("Error creating results file ", ex);
        }
    }

    private double getTotal(List<Double> set) {
        double total = 0;
        for (Double value:set){
            total += value;
        }
        return total;
    }
}
